package org.acm.seguin.refactor.method; import java.util.Iterator; import org.acm.seguin.parser.ChildrenVisitor; import org.acm.seguin.parser.ast.ASTArgumentList; import org.acm.seguin.parser.ast.ASTArguments; import org.acm.seguin.parser.ast.ASTExpression; import org.acm.seguin.parser.ast.ASTFormalParameter; import org.acm.seguin.parser.ast.ASTFormalParameters; import org.acm.seguin.parser.ast.ASTName; import org.acm.seguin.parser.ast.ASTPrimaryExpression; import org.acm.seguin.parser.ast.ASTPrimaryPrefix; import org.acm.seguin.parser.ast.ASTPrimarySuffix; import org.acm.seguin.parser.ast.ASTType; import org.acm.seguin.parser.ast.ASTUnmodifiedClassDeclaration; import org.acm.seguin.parser.ast.ASTUnmodifiedInterfaceDeclaration; import org.acm.seguin.parser.ast.ASTVariableDeclaratorId; import org.acm.seguin.summary.FieldSummary; import org.acm.seguin.summary.LocalVariableSummary; import org.acm.seguin.summary.MethodSummary; import org.acm.seguin.summary.ParameterSummary; import org.acm.seguin.summary.Summary; import org.acm.seguin.summary.TypeSummary; import org.acm.seguin.summary.query.FieldQuery; /** * Visitor that prepares a method for being incorporated into another class. * *@author Chris Seguin */ public class MoveMethodVisitor extends ChildrenVisitor { private MethodSummary methodSummary; private TypeSummary typeSummary; private Summary destination; /** * Constructor for the MoveMethodVisitor object * *@param initType Description of Parameter *@param initMethod Description of Parameter *@param initDest Description of Parameter */ public MoveMethodVisitor(TypeSummary initType, MethodSummary initMethod, Summary initDest) { typeSummary = initType; methodSummary = initMethod; destination = initDest; } /** * Don't go into any class definitions * *@param node Description of Parameter *@param data Description of Parameter *@return Description of the Returned Value */ public Object visit(ASTUnmodifiedClassDeclaration node, Object data) { return data; } /** * Don't go into any interface definitions * *@param node Description of Parameter *@param data Description of Parameter *@return Description of the Returned Value */ public Object visit(ASTUnmodifiedInterfaceDeclaration node, Object data) { return data; } /** * To visit a node * *@param node The node we are visiting *@param data The rename type data *@return The rename type data */ public Object visit(ASTFormalParameters node, Object data) { if (destination instanceof ParameterSummary) { String name = destination.getName(); int last = node.jjtGetNumChildren(); for (int ndx = 0; ndx < last; ndx++) { ASTFormalParameter param = (ASTFormalParameter) node.jjtGetChild(ndx); ASTVariableDeclaratorId decl = (ASTVariableDeclaratorId) param.jjtGetChild(1); if (decl.getName().equals(name)) { node.jjtDeleteChild(ndx); ndx--; last--; } } if (ObjectReference.isReferenced(methodSummary)) { ASTFormalParameter newParam = new ASTFormalParameter(0); ASTType type = new ASTType(0); newParam.jjtAddChild(type, 0); ASTName nameNode = new ASTName(0); String typeName = typeSummary.getName(); nameNode.addNamePart(typeName); type.jjtAddChild(nameNode, 0); ASTVariableDeclaratorId id = new ASTVariableDeclaratorId(0); id.setName(typeName.substring(0, 1).toLowerCase() + typeName.substring(1)); newParam.jjtAddChild(id, 1); last = node.jjtGetNumChildren(); node.jjtAddChild(newParam, last); } } return data; } /** * Description of the Method * *@param node Description of Parameter *@param data Description of Parameter *@return Description of the Returned Value */ public Object visit(ASTExpression node, Object data) { if (node.jjtGetNumChildren() > 1) { Object result = super.visit(node, Boolean.TRUE); if (result != null) { ASTArguments args = (ASTArguments) result; ASTArgumentList list = new ASTArgumentList(0); args.jjtAddChild(list, 0); list.jjtAddChild(node.jjtGetChild(2), 0); node.jjtDeleteChild(2); node.jjtDeleteChild(1); } return null; } else { return super.visit(node, Boolean.FALSE); } } /** * Visit method for primary expressions * *@param node the node we are visiting *@param data Description of Parameter *@return Description of the Returned Value */ public Object visit(ASTPrimaryExpression node, Object data) { Object result = null; if (destination instanceof ParameterSummary) { String parameterName = destination.getName(); ASTPrimaryPrefix prefix = (ASTPrimaryPrefix) node.jjtGetChild(0); if ("this".equals(prefix.getName())) { ASTName nameNode = new ASTName(0); nameNode.addNamePart(getReplacementVariableName()); prefix.jjtAddChild(nameNode, 0); } else if (isMethod(node, prefix)) { updatePrimaryPrefix(prefix, parameterName); } else if (isVariable(node, prefix)) { ASTName nameNode = (ASTName) prefix.jjtGetChild(0); if (!isLocalVariable(nameNode.getNamePart(0))) { Boolean value = (Boolean) data; result = updatePrivateField(node, prefix, nameNode, parameterName, value.booleanValue()); } } } super.visit(node, data); return result; } /** * Determine if we have a method * *@param node Description of Parameter *@param prefix Description of Parameter *@return The Method value */ private boolean isMethod(ASTPrimaryExpression node, ASTPrimaryPrefix prefix) { return (node.jjtGetNumChildren() > 1) && (node.jjtGetChild(1).jjtGetChild(0) instanceof ASTArguments) && (prefix.jjtGetChild(0) instanceof ASTName); } /** * Determine if we have a field access * *@param node Description of Parameter *@param prefix Description of Parameter *@return The Variable value */ private boolean isVariable(ASTPrimaryExpression node, ASTPrimaryPrefix prefix) { return (node.jjtGetNumChildren() == 1) && (prefix.jjtGetChild(0) instanceof ASTName); } /** * Checks if the value is a local variable * *@param name the name of the variable *@return true if the name is a local variable */ private boolean isLocalVariable(String name) { Iterator iter = methodSummary.getDependencies(); if (iter != null) { while (iter.hasNext()) { Summary next = (Summary) iter.next(); if (next instanceof LocalVariableSummary) { LocalVariableSummary lvs = (LocalVariableSummary) next; if (lvs.getName().equals(name)) { return true; } } } } return false; } /** * Gets the name of the getter for the field * *@param summary the field summary *@return the getter */ private String getFieldGetter(FieldSummary summary) { String typeName = summary.getType(); String prefix = "get"; if (typeName.equalsIgnoreCase("boolean")) { prefix = "is"; } String name = summary.getName(); return prefix + name.substring(0, 1).toUpperCase() + name.substring(1); } /** * Gets the name of the setter for the field * *@param summary the field summary *@return the setter */ private String getFieldSetter(FieldSummary summary) { String prefix = "set"; String name = summary.getName(); return prefix + name.substring(0, 1).toUpperCase() + name.substring(1); } /** * Update the primary prefix * *@param prefix Description of Parameter *@param parameterName Description of Parameter */ private void updatePrimaryPrefix(ASTPrimaryPrefix prefix, String parameterName) { ASTName nameNode = (ASTName) prefix.jjtGetChild(0); if (nameNode.getNameSize() == 1) { updateLocalReferences(prefix, nameNode); } else if ((nameNode.getNameSize() == 2) && (nameNode.getNamePart(0).equals(parameterName))) { updateParameterReferences(prefix, nameNode); } } /** * Updates the local references * *@param prefix Description of Parameter *@param nameNode Description of Parameter */ private void updateLocalReferences(ASTPrimaryPrefix prefix, ASTName nameNode) { ASTName newName = new ASTName(0); newName.addNamePart(getReplacementVariableName()); newName.addNamePart(nameNode.getNamePart(0)); prefix.jjtAddChild(newName, 0); } /** * Gets the variable name that is replacing "this" in the method * *@return the name of the variable */ private String getReplacementVariableName() { String typeName = typeSummary.getName(); return typeName.substring(0, 1).toLowerCase() + typeName.substring(1); } /** * Updates references to the parameter * *@param prefix Description of Parameter *@param nameNode Description of Parameter */ private void updateParameterReferences(ASTPrimaryPrefix prefix, ASTName nameNode) { ASTName newName = new ASTName(0); newName.addNamePart(nameNode.getNamePart(1)); prefix.jjtAddChild(newName, 0); } /** * Updates a private field * *@param primary Description of Parameter *@param prefix Description of Parameter *@param nameNode Description of Parameter *@param parameterName Description of Parameter *@param isSetter Description of Parameter *@return Description of the Returned Value */ private Object updatePrivateField(ASTPrimaryExpression primary, ASTPrimaryPrefix prefix, ASTName nameNode, String parameterName, boolean isSetter) { if (nameNode.getNameSize() == 1) { String name = nameNode.getNamePart(0); FieldSummary field = FieldQuery.find(typeSummary, name); if (field.getModifiers().isPrivate()) { ASTName newName = new ASTName(0); newName.addNamePart(getReplacementVariableName()); newName.addNamePart(isSetter ? getFieldSetter(field) : getFieldGetter(field)); prefix.jjtAddChild(newName, 0); ASTPrimarySuffix suffix = new ASTPrimarySuffix(0); ASTArguments args = new ASTArguments(0); suffix.jjtAddChild(args, 0); primary.jjtInsertChild(suffix, 1); return args; } } updatePrimaryPrefix(prefix, parameterName); return null; } }